/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/*
 * performance.cpp
 *
 * Measure performance of classifier
 */
#include <cv.h>
#include <highgui.h>

#include <cstdio>
#include <cmath>
#include <ctime>

#ifdef _WIN32
/* use clock() function insted of time() */
#define time( arg ) (((double) clock()) / CLOCKS_PER_SEC)
#endif /* _WIN32 */

#ifndef PATH_MAX
#define PATH_MAX 512
#endif /* PATH_MAX */

typedef struct HidCascade {
	int size;
	int count;
} HidCascade;

typedef struct ObjectPos {
	float x;
	float y;
	float width;
	int found;    /* for reference */
	int neghbors;
} ObjectPos;

int main( int argc, char* argv[] ) {
	int i, j;
	char* classifierdir = NULL;
	//char* samplesdir    = NULL;

	int saveDetected = 1;
	double scale_factor = 1.2;
	float maxSizeDiff = 1.5F;
	float maxPosDiff  = 0.3F;

	/* number of stages. if <=0 all stages are used */
	int nos = -1, nos0;

	int width  = 24;
	int height = 24;

	int rocsize;

	FILE* info;
	char* infoname;
	char fullname[PATH_MAX];
	char detfilename[PATH_MAX];
	char* filename;
	char detname[] = "det-";

	CvHaarClassifierCascade* cascade;
	CvMemStorage* storage;
	CvSeq* objects;

	double totaltime;

	infoname = (char*)"";
	rocsize = 40;
	if ( argc == 1 ) {
		printf( "Usage: %s\n  -data <classifier_directory_name>\n"
				"  -info <collection_file_name>\n"
				"  [-maxSizeDiff <max_size_difference = %f>]\n"
				"  [-maxPosDiff <max_position_difference = %f>]\n"
				"  [-sf <scale_factor = %f>]\n"
				"  [-ni]\n"
				"  [-nos <number_of_stages = %d>]\n"
				"  [-rs <roc_size = %d>]\n"
				"  [-w <sample_width = %d>]\n"
				"  [-h <sample_height = %d>]\n",
				argv[0], maxSizeDiff, maxPosDiff, scale_factor, nos, rocsize,
				width, height );

		return 0;
	}

	for ( i = 1; i < argc; i++ ) {
		if ( !strcmp( argv[i], "-data" ) ) {
			classifierdir = argv[++i];
		} else if ( !strcmp( argv[i], "-info" ) ) {
			infoname = argv[++i];
		} else if ( !strcmp( argv[i], "-maxSizeDiff" ) ) {
			maxSizeDiff = (float) atof( argv[++i] );
		} else if ( !strcmp( argv[i], "-maxPosDiff" ) ) {
			maxPosDiff = (float) atof( argv[++i] );
		} else if ( !strcmp( argv[i], "-sf" ) ) {
			scale_factor = atof( argv[++i] );
		} else if ( !strcmp( argv[i], "-ni" ) ) {
			saveDetected = 0;
		} else if ( !strcmp( argv[i], "-nos" ) ) {
			nos = atoi( argv[++i] );
		} else if ( !strcmp( argv[i], "-rs" ) ) {
			rocsize = atoi( argv[++i] );
		} else if ( !strcmp( argv[i], "-w" ) ) {
			width = atoi( argv[++i] );
		} else if ( !strcmp( argv[i], "-h" ) ) {
			height = atoi( argv[++i] );
		}
	}

	cascade = cvLoadHaarClassifierCascade( classifierdir, cvSize( width, height ) );
	if ( cascade == NULL ) {
		printf( "Unable to load classifier from %s\n", classifierdir );

		return 1;
	}

	int* numclassifiers = new int[cascade->count];
	numclassifiers[0] = cascade->stage_classifier[0].count;
	for ( i = 1; i < cascade->count; i++ ) {
		numclassifiers[i] = numclassifiers[i-1] + cascade->stage_classifier[i].count;
	}

	storage = cvCreateMemStorage();

	nos0 = cascade->count;
	if ( nos <= 0 ) {
		nos = nos0;
	}

	strcpy( fullname, infoname );
	filename = strrchr( fullname, '\\' );
	if ( filename == NULL ) {
		filename = strrchr( fullname, '/' );
	}
	if ( filename == NULL ) {
		filename = fullname;
	} else {
		filename++;
	}

	info = fopen( infoname, "r" );
	totaltime = 0.0;
	if ( info != NULL ) {
		int x, y, width, height;
		IplImage* img;
		int hits, missed, falseAlarms;
		int totalHits, totalMissed, totalFalseAlarms;
		int found;
		float distance;

		int refcount;
		ObjectPos* ref;
		int detcount;
		ObjectPos* det;
		int error = 0;

		int* pos;
		int* neg;

		pos = (int*) cvAlloc( rocsize * sizeof( *pos ) );
		neg = (int*) cvAlloc( rocsize * sizeof( *neg ) );
		for ( i = 0; i < rocsize; i++ ) { pos[i] = neg[i] = 0; }

		printf( "+================================+======+======+======+\n" );
		printf( "|            File Name           | Hits |Missed| False|\n" );
		printf( "+================================+======+======+======+\n" );

		totalHits = totalMissed = totalFalseAlarms = 0;
		while ( !feof( info ) ) {
			if ( fscanf( info, "%s %d", filename, &refcount ) != 2 || refcount <= 0 ) { break; }

			img = cvLoadImage( fullname );
			if ( !img ) { continue; }

			ref = (ObjectPos*) cvAlloc( refcount * sizeof( *ref ) );
			for ( i = 0; i < refcount; i++ ) {
				error = (fscanf( info, "%d %d %d %d", &x, &y, &width, &height ) != 4);
				if ( error ) { break; }
				ref[i].x = 0.5F * width  + x;
				ref[i].y = 0.5F * height + y;
				ref[i].width = sqrtf( 0.5F * (width * width + height * height) );
				ref[i].found = 0;
				ref[i].neghbors = 0;
			}
			if ( !error ) {
				cvClearMemStorage( storage );

				cascade->count = nos;
				totaltime -= time( 0 );
				objects = cvHaarDetectObjects( img, cascade, storage, scale_factor, 1 );
				totaltime += time( 0 );
				cascade->count = nos0;

				detcount = ( objects ? objects->total : 0);
				det = (detcount > 0) ?
					  ( (ObjectPos*)cvAlloc( detcount * sizeof( *det )) ) : NULL;
				hits = missed = falseAlarms = 0;
				for ( i = 0; i < detcount; i++ ) {
					CvAvgComp r = *((CvAvgComp*) cvGetSeqElem( objects, i ));
					det[i].x = 0.5F * r.rect.width  + r.rect.x;
					det[i].y = 0.5F * r.rect.height + r.rect.y;
					det[i].width = sqrtf( 0.5F * (r.rect.width * r.rect.width +
												  r.rect.height * r.rect.height) );
					det[i].neghbors = r.neighbors;

					if ( saveDetected ) {
						cvRectangle( img, cvPoint( r.rect.x, r.rect.y ),
									 cvPoint( r.rect.x + r.rect.width, r.rect.y + r.rect.height ),
									 CV_RGB( 255, 0, 0 ), 3 );
					}

					found = 0;
					for ( j = 0; j < refcount; j++ ) {
						distance = sqrtf( (det[i].x - ref[j].x) * (det[i].x - ref[j].x) +
										  (det[i].y - ref[j].y) * (det[i].y - ref[j].y) );
						if ( (distance < ref[j].width * maxPosDiff) &&
								(det[i].width > ref[j].width / maxSizeDiff) &&
								(det[i].width < ref[j].width * maxSizeDiff) ) {
							ref[j].found = 1;
							ref[j].neghbors = MAX( ref[j].neghbors, det[i].neghbors );
							found = 1;
						}
					}
					if ( !found ) {
						falseAlarms++;
						neg[MIN(det[i].neghbors, rocsize - 1)]++;
					}
				}
				for ( j = 0; j < refcount; j++ ) {
					if ( ref[j].found ) {
						hits++;
						pos[MIN(ref[j].neghbors, rocsize - 1)]++;
					} else {
						missed++;
					}
				}

				totalHits += hits;
				totalMissed += missed;
				totalFalseAlarms += falseAlarms;
				printf( "|%32.32s|%6d|%6d|%6d|\n", filename, hits, missed, falseAlarms );
				printf( "+--------------------------------+------+------+------+\n" );
				fflush( stdout );

				if ( saveDetected ) {
					strcpy( detfilename, detname );
					strcat( detfilename, filename );
					strcpy( filename, detfilename );
					cvvSaveImage( fullname, img );
				}

				if ( det ) { cvFree( &det ); det = NULL; }
			} /* if( !error ) */

			cvReleaseImage( &img );
			cvFree( &ref );
		}
		fclose( info );

		printf( "|%32.32s|%6d|%6d|%6d|\n", "Total",
				totalHits, totalMissed, totalFalseAlarms );
		printf( "+================================+======+======+======+\n" );
		printf( "Number of stages: %d\n", nos );
		printf( "Number of weak classifiers: %d\n", numclassifiers[nos - 1] );
		printf( "Total time: %f\n", totaltime );

		/* print ROC to stdout */
		for ( i = rocsize - 1; i > 0; i-- ) {
			pos[i-1] += pos[i];
			neg[i-1] += neg[i];
		}
		fprintf( stderr, "%d\n", nos );
		for ( i = 0; i < rocsize; i++ ) {
			fprintf( stderr, "\t%d\t%d\t%f\t%f\n", pos[i], neg[i],
					 ((float)pos[i]) / (totalHits + totalMissed),
					 ((float)neg[i]) / (totalHits + totalMissed) );
		}

		cvFree( &pos );
		cvFree( &neg );
	}

	delete[] numclassifiers;

	cvReleaseHaarClassifierCascade( &cascade );
	cvReleaseMemStorage( &storage );

	return 0;
}

